﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Luna.Framework.Manifest;
using System.Resources;
using Luna;
using System.Reflection;
using Luna.Framework.Impl.UriResolvers;
using Luna.Framework.Impl;

namespace Luna.Framework {
	public sealed class Framework {

		private static LunaApplication application;
		private static IManifestManager manifest;

		/// <summary>
		/// Return the root context of the application.
		/// </summary>
		public static IAppContext RootContext { get; private set; }

		/// <summary>
		/// Return a reference to Application object which represents the application.
		/// </summary>
		public static LunaApplication Application {
			get { return application; }
			set {
				// user is allowed to set value to Application once, at starting of an application.
				if (application == null) application = value;
			}
		}

		/// <summary>
		/// Return the Manifest Manager object of the application
		/// </summary>
		public static IManifestManager Manifest {
			get { return manifest; }
			set {
				if (manifest == null) manifest = value;
			}
		}

		public static void initialize(string defaultManifestFile) {
			RootContext = new DefaultContext(null, "$root");
			Manifest = new ManifestManager() { Context = RootContext };
			if (defaultManifestFile != null && defaultManifestFile.fileExists())
				Manifest.loadManifest(defaultManifestFile);

			RootContext.addService(new NewUriResolver());
			RootContext.addService(new ResourceUriResolver());

			stringCaches = new Dictionary<string, string>();
			//stringCaches["Assembly"] = Assembly.GetEntryAssembly().FullName;
		}

		public static T createApplication<T>(string appId, T app) where T : Luna.LunaApplication {
			if (manifest != null) {
				IConfigurationManifest appManifest = manifest.getManifest(appId);
				if (appManifest != null)
					app = manifest.applyManifest<T>(appManifest, RootContext, app);
			}
			Application = app;
			RootContext.Host = app;
			return app;
		}

		public static string expandString(string s) {
			StringBuilder res = new StringBuilder();
			int idx = s.IndexOf('%');
			int lastidx = 0;
			for (; idx >= 0; idx = s.IndexOf('%', idx + 1)) {
				res.Append(s.Substring(lastidx, idx - lastidx));
				int eidx = s.IndexOf('%', idx + 1);
				if (eidx == idx + 1)
					res.Append('%');
				else {
					string str = s.Substring(idx + 1, eidx - idx - 1);
					res.Append(lookupString(str));
					idx = eidx;
				}
				lastidx = eidx + 1;
			}
			if (lastidx < s.Length)
				res.Append(s.Substring(lastidx, s.Length - lastidx));
			return res.ToString();
		}

		private static Dictionary<string, string> stringCaches;
		
		private static string lookupString(string key) {
			if (stringCaches.ContainsKey(key))
				return stringCaches[key];
			else {
				string s = Manifest.lookupString(key);
				stringCaches[key] = s; // prevent cycle ref.
				s = expandString(s); // and expand the string if needed
				stringCaches[key] = s; // update the cache with new expanded string
				return s;
			}
		}

		public static object resolveObject(IAppContext context, Uri uri, object data) {
			string scheme = uri.Scheme;
			if (context == null)
				context = RootContext;

			IEnumerable<IUriResolver> resolversEnum = context.findServices<IUriResolver>(s => s.supportScheme(scheme));

			List<IUriResolver> resolvers = resolversEnum.ToList<IUriResolver>();
			for(int i = resolvers.Count - 1; i >= 0; i--)
				if (resolvers[i].supportScheme(scheme)) {       
					object obj = resolvers[i].resolveObject(context, uri, data);
					if (obj != null)
						return obj;
				}	
			return null;
		}
	}
}
